/*
 * @(#)HtmlEscapeSequence.java	1.0 05/01/07
 *
 * Copyright 2007 Fabio Gasparetti. All rights reserved.
 */

package org.tabularium.text.html;

import java.util.*;
import java.util.regex.*;

/**
 * Removes html escape sequences (e.g., &nbsp;) from text.
 *
 * @author  Fabio Gasparetti
 * @version 1.0, 05/01/07
 */

public class HtmlEscapeSequence {
	// below this length do not match anything
	static protected int DEFAULT_MIN_SEQ_LENGTH = 4;
	// html escape sequences 
	static protected Properties escapeTableCS = new Properties();
	static protected Properties escapeTableCI = new Properties();
	// default escape sequences.. see iso8859-1 table
	static private final String[] ESCAPETBLCS = {
		// case sensitive
		"Agrave", "192", 
		"Aacute", "193", 
		"Acirc", "194", 
		"Atilde", "195", 
		"Auml", "196", 
		"Aring", "197", 
		"AElig", "198", 
		"Ccedil", "199", 
		"Egrave", "200", 
		"Eacute", "201", 
		"Ecirc", "202", 
		"Euml", "203", 
		"Igrave", "204", 
		"Iacute", "205", 
		"Icirc", "206", 
		"Iuml", "207", 
		"ETH", "208", 
		"Ntilde", "209", 
		"Ograve", "210", 
		"Oacute", "211", 
		"Ocirc", "212", 
		"Otilde", "213", 
		"Ouml", "214", 
		"Oslash", "216", 
		"Ugrave", "217", 
		"Uacute", "218", 
		"Ucirc", "219", 
		"Uuml", "220", 
		"Yacute", "221", 
		"THORN", "222", 
		"agrave", "224", 
		"aacute", "225", 
		"acirc", "226", 
		"atilde", "227", 
		"auml", "228", 
		"aring", "229", 
		"aelig", "230", 
		"ccedil", "231", 
		"egrave", "232", 
		"eacute", "233", 
		"ecirc", "234", 
		"euml", "235", 
		"igrave", "236", 
		"iacute", "237", 
		"icirc", "238", 
		"iuml", "239", 
		"eth", "240", 
		"ntilde", "241", 
		"ograve", "242", 
		"oacute", "243", 
		"ocirc", "244", 
		"otilde", "245", 
		"ouml", "246", 
		"oslash", "248", 
		"ugrave", "249", 
		"uacute", "250", 
		"ucirc", "251", 
		"uuml", "252", 
		"yacute", "253", 
		"thorn", "254", 
		"yuml", "255", 
		"Oelig", "338", 
		"oelig", "339", 
		"Scaron", "352", 
		"scaron", "353", 
		"Yuml", "376", 
		"Alpha", "913", 
		"Beta", "914", 
		"Gamma", "915", 
		"Delta", "916", 
		"Epsilon", "917", 
		"Zeta", "918", 
		"Eta", "919", 
		"Theta", "920", 
		"Iota", "921", 
		"Kappa", "922", 
		"Lambda", "923", 
		"Mu", "924", 
		"Nu", "925", 
		"Xi", "926", 
		"Omicron", "927", 
		"Pi", "928", 
		"Rho", "929", 
		"Sigma", "931", 
		"Tau", "932", 
		"Upsilon", "933", 
		"Phi", "934", 
		"Chi", "935", 
		"Psi", "936", 
		"Omega", "937", 
		"alpha", "945", 
		"beta", "946", 
		"gamma", "947", 
		"delta", "948", 
		"epsilon", "949", 
		"zeta", "950", 
		"eta", "951", 
		"theta", "952", 
		"iota", "953", 
		"kappa", "954", 
		"lambda", "955", 
		"mu", "956", 
		"nu", "957", 
		"xi", "958", 
		"omicron", "959", 
		"pi", "960", 
		"rho", "961", 
		"sigma", "963", 
		"tau", "964", 
		"upsilon", "965", 
		"phi", "966", 
		"chi", "967", 
		"psi", "968", 
		"omega", "969", 
		"dagger", "8224", 
		"Dagger", "8225", 
		"prime", "8242", 
		"Prime", "8243", 
		"larr", "8592", 
		"uarr", "8593", 
		"rarr", "8594", 
		"darr", "8595", 
		"harr", "8596", 
		"lArr", "8656", 
		"uArr", "8657", 
		"rArr", "8658", 
		"dArr", "8659", 
		"hArr", "8660"};

	static private final String[] ESCAPETBLCI = {
		// case insensitive
		"quot", "34", 
		"amp", "38", 
		"lt", "60", 
		"gt", "62", 
		"nbsp", "160", 
		"iexcl", "161", 
		"cent", "162", 
		"pound", "163", 
		"curren", "164", 
		"yen", "165", 
		"brvbar", "166", 
		"sect", "167", 
		"uml", "168", 
		"copy", "169", 
		"ordf", "170", 
		"laquo", "171", 
		"not", "172", 
		"shy", "173", 
		"reg", "174", 
		"macr", "175", 
		"deg", "176", 
		"plusmn", "177", 
		"sup2", "178", 
		"sup3", "179", 
		"acute", "180", 
		"micro", "181", 
		"para", "182", 
		"middot", "183", 
		"cedil", "184", 
		"sup1", "185", 
		"ordm", "186", 
		"raquo", "187", 
		"frac14", "188", 
		"frac12", "189", 
		"frac34", "190", 
		"iquest", "191", 
		"times", "215", 
		"szlig", "223", 
		"divide", "247", 
		"fnof", "402", 
		"circ", "710", 
		"tilde", "732", 
		"sigmaf", "962", 
		"thetasym", "977", 
		"upsih", "978", 
		"piv", "982", 
		"ensp", "8194", 
		"emsp", "8195", 
		"thinsp", "8201", 
		"zwnj", "8204", 
		"zwj", "8205", 
		"lrm", "8206", 
		"rlm", "8207", 
		"ndash", "8211", 
		"mdash", "8212", 
		"lsquo", "8216", 
		"rsquo", "8217", 
		"sbquo", "8218", 
		"ldquo", "8220", 
		"rdquo", "8221", 
		"bdquo", "8222", 
		"bull", "8226", 
		"hellip", "8230", 
		"permil", "8240", 
		"lsaquo", "8249", 
		"rsaquo", "8250", 
		"oline", "8254", 
		"frasl", "8260", 
		"euro", "8364", 
		"image", "8465", 
		"weierp", "8472",		
		"real", "8476", 
		"trade", "8482", 
		"alefsym", "8501", 
		"crarr", "8629", 
		"forall", "8704",  
		"part", "8706", 
		"exist", "8707", 
		"empty", "8709", 
		"nabla", "8711", 
		"isin", "8712", 
		"notin", "8713", 
		"ni", "8715", 
		"prod", "8719", 
		"sum", "8721", 
		"minus", "8722", 
		"lowast", "8727", 
		"radic", "8730", 
		"prop", "8733", 
		"infin", "8734", 
		"ang", "8736", 
		"and", "8743", 
		"or", "8744", 
		"cap", "8745", 
		"cup", "8746", 
		"int", "8747", 
		"there4", "8756", 
		"sim", "8764", 
		"cong", "8773", 
		"asymp", "8776", 
		"ne", "8800", 
		"equiv", "8801", 
		"le", "8804", 
		"ge", "8805", 
		"sub", "8834", 
		"sup", "8835", 
		"nsub", "8836", 
		"sube", "8838", 
		"supe", "8839", 
		"oplus", "8853", 
		"otimes", "8855", 
		"perp", "8869", 
		"sdot", "8901", 
		"lceil", "8968", 
		"rceil", "8969", 
		"lfloor", "8970", 
		"rfloor", "8971", 
		"lang", "9001", 
		"rang", "9002", 
		"loz", "9674", 
		"spades", "9824", 
		"clubs", "9827", 
		"hearts", "9829", 
		"diams", "9830",
		// if there are case sensitive sequences that do not match
		// the previous table, turn it lower-case and see if they
		// match any sequence
		"aacute", "225", 
		"acirc", "226", 
		"aelig", "230", 
		"agrave", "224", 
		"alpha", "945", 
		"aring", "229", 
		"atilde", "227", 
		"auml", "228", 
		"beta", "946", 
		"ccedil", "231", 
		"chi", "967", 
		"dagger", "8224", 
		"darr", "8595", 
		"delta", "948", 
		"eacute", "233", 
		"ecirc", "234", 
		"egrave", "232", 
		"epsilon", "949", 
		"eta", "951", 
		"eth", "240", 
		"euml", "235", 
		"gamma", "947", 
		"harr", "8596", 
		"iacute", "237", 
		"icirc", "238", 
		"igrave", "236", 
		"iota", "953", 
		"iuml", "239", 
		"kappa", "954", 
		"lambda", "955", 
		"larr", "8592", 
		"mu", "956", 
		"ntilde", "241", 
		"nu", "957", 
		"oacute", "243", 
		"ocirc", "244", 
		"oelig", "339", 
		"ograve", "242", 
		"omega", "969", 
		"omicron", "959", 
		"oslash", "248", 
		"otilde", "245", 
		"ouml", "246", 
		"phi", "966", 
		"pi", "960", 
		"prime", "8242", 
		"psi", "968", 
		"rarr", "8594", 
		"rho", "961", 
		"scaron", "353", 
		"sigma", "963", 
		"tau", "964", 
		"theta", "952", 
		"thorn", "254", 
		"uacute", "250", 
		"uarr", "8593", 
		"ucirc", "251", 
		"ugrave", "249", 
		"upsilon", "965", 
		"uuml", "252", 
		"xi", "958", 
		"yacute", "253", 
		"yuml", "255", 
		"zeta", "950" };
	
	static {
		// set up the escapeTable maps
		for (int i = 0; i < ESCAPETBLCS.length; i += 2)
			escapeTableCS.setProperty(ESCAPETBLCS[i], ESCAPETBLCS[i + 1]);
		for (int i = 0; i < ESCAPETBLCI.length; i += 2)
			escapeTableCI.setProperty(ESCAPETBLCI[i], ESCAPETBLCI[i + 1]);
	}

	protected HtmlEscapeSequence() {
		
	}

	/**
	 * Removes html escaped sequences.
	 */
	public static String removeEscapedCh(String htmltext) {
		if (htmltext.length() < DEFAULT_MIN_SEQ_LENGTH) 
			return htmltext;
		// converts &#nn; escape characters
		Pattern p = Pattern.compile("[&][#]([0-9]+)[;]");
		Matcher m = p.matcher(htmltext);
		StringBuffer sb = new StringBuffer();
		boolean go = m.find();
		while (go) {
			String s = m.group(1);
			char ch = (char) Integer.parseInt(s, 10);
			if (ch != '$') {
				m.appendReplacement(sb, "" + ch);
			} else {
				m.appendReplacement(sb, "\\" + ch);
			}
			go = m.find();
		}
		m.appendTail(sb);
		// converts &#hex; escape characters
		p = Pattern.compile("[&][#][xX]([0-9A-Fa-f]+)[;]");
		m = p.matcher(sb.toString());
		sb = new StringBuffer();
		go = m.find();
		while (go) {
			String s = m.group(1);
			char ch = (char) Integer.parseInt(s, 16);
			if (ch != '$') {
				m.appendReplacement(sb, "" + ch);
			} else {
				m.appendReplacement(sb, "\\" + ch);
			}
			go = m.find();
		}
		m.appendTail(sb);
		// converts mnemonic escape characters
		p = Pattern.compile("[&]([a-zA-Z0-9]+)[;]");
		m = p.matcher(sb.toString());
		sb = new StringBuffer();
		go = m.find();
		while (go) {
			String s1 = m.group(1);
			String s = escapeTableCS.getProperty(s1);
			if (s != null) {
				char ch = (char) Integer.parseInt(s, 10);
				if (ch != '$') {
					m.appendReplacement(sb, "" + ch);
				} else {
					m.appendReplacement(sb, "\\" + ch);
				}
			} else {
				s = escapeTableCI.getProperty(s1.toLowerCase());
				if (s != null) {
					char ch = (char) Integer.parseInt(s, 10);
					if (ch != '$') {
						m.appendReplacement(sb, "" + ch);
					} else {
						m.appendReplacement(sb, "\\" + ch);
					}
				} 
			}
			go = m.find();
		}
		m.appendTail(sb);

		return sb.toString();
	}
}


/*
TODO: It could be useful load the tables from file?
 
try {
   Properties p = new Properties();
   p.load(new java.io.FileInputStream("htmlcode.properties"));
   HtmlUtils.setEscapeTable(p);
            }
            catch (Exception ex) {
   System.out.println("unable to load htmlcode table");
            }
*/

